﻿using System;
using System.Collections.Generic; 
using System.Web;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.ComponentModel;
using System.Globalization;
using Newtonsoft.Json;

namespace Sharp
{

    public interface IField {
        IField Set(object val);

    }
   
    public class FieldConverter<T> : Newtonsoft.Json.Converters.CustomCreationConverter<IField>
    {
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            foreach (var p in typeof(T).GetProperties())
            {
                // Initializes all the Field<>
                if (p.Name.Like(reader.Path) && p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(Field<>))
                {
                    var a = (IValTest[])p.GetCustomAttributes(typeof(IValTest), true);
                    var pars = new Object[] { a }.ToList();

                    object value;
                    if (reader.ValueType != null)
                    {value = reader.Value;}
                    else
                    {
                        Field<object> temp = (Field<object>)Create(objectType);
                        serializer.Populate(reader, temp);
                        value = temp.Value;
                    }

                    if (value == null && HttpContext.Current != null)
                    {
                        foreach (var val in System.Web.Routing.RouteTable.Routes.GetRouteData((HttpContextBase)new HttpContextWrapper(HttpContext.Current)).Values)
                        {
                            if (p.Name.Like(val.Key)) //case insensitive
                            {
                                value = val.Value;
                                break;
                            }
                        }
                    }

                    pars.Add(value);
                    pars.Add(p.Name);
                    // Create Instance of property
                    var newobj = Activator.CreateInstance(p.PropertyType, pars.ToArray());
                    return newobj;
                }
            }

            var newval = Create(objectType);
            serializer.Populate(reader, newval);
            return newval;
        }

        public override IField Create(Type objectType)
        {
            return (IField)new Field<object>(new IValTest[] {});
        }
    }

    /// <summary>
    /// Validity
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [DataContract]
    public class Field<T> : IField
    {
        /// <summary>
        /// Constructs an instance and initializes all the Val<> properties
        /// </summary>
        /// <returns></returns>
        public static T invoke()
        {
            T t = (T)Activator.CreateInstance(typeof(T));
            foreach (var p in t.GetType().GetProperties())
            {
                // Initializes all the Val<>
                if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(Field<>))
                {
                    var a = (IValTest[])p.GetCustomAttributes(typeof(IValTest), true);
                    var pars = new Object[] { a }.ToList();

                    if (HttpContext.Current != null)
                    {
                        var value = HttpContext.Current.Request.Form[p.Name] ??
                            HttpContext.Current.Request.QueryString[p.Name] ??
                            (p.Name.Like("id") ? HttpContext.Current.Request.Path.Split("/").Last() : null);
                        // if (value != null) //you cant use default properties at runtime, so always add a value even null.
                        pars.Add(value);
                    }
                    // Create Instance of property
                    p.SetValue(t, Activator.CreateInstance(p.PropertyType, pars.ToArray()), null);
                }
            }
            return t;
        }

        [DataMember]
        public List<string> Errors;

        public string FieldName { get; set; }

        [DataMember]
        public new bool IsValid { get { return Errors.Count == 0; } set { } }

        private T _Value;
        [DataMember]
        public T Value { get { return _Value; } set { Set(value); } }

        /// <summary>
        /// Runs all of the assigned tests.
        /// </summary>
        /// <param name="value">Value to set</param>
        /// <returns>Only true if all the tests passed</returns>
        public IField Set(object value)
        {
            Errors = new List<string>();

            Type aval = this.GetType();
            bool noerrorsflag = true;
            foreach (IValTest ivaltest in Tests ?? new IValTest[] {})
            {
                noerrorsflag = noerrorsflag && ivaltest.Test(value, Errors);
            }

            //get handlers Statebag and set error
            if (HttpContext.Current != null && !noerrorsflag)
            {
                var handler = new HttpContextWrapper(HttpContext.Current).CurrentHandler;
                if (handler is Handler)
                {
                    (handler as Handler).sb.Valid = false;
                    (handler as Handler).sb.ErrorMsg = "Field error: " + FieldName ?? "" ;
                    //(handler as Handler).sb.ErrorMsg = FieldName + ": (" + value.ToString() + ") " + String.Join(", ", Errors.ToArray());
                }
            }

            //assign value using semi-intelligent type detection, at some point just use a built in deserializer
            {
                if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
                {
                    var typename = typeof(T).Name;
                    if (typeof(T).IsGenericType) //handle nullable types
                        if (typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>))
                            typename = typeof(T).GetGenericArguments()[0].Name;

                    if (typename == typeof(Boolean).Name)
                        _Value = (T)(object)Boolean.Parse(value.ToString());
                    else if (typeof(T).IsAssignableFrom(value.GetType()))
                        _Value = (T)value;

                    if (value != null && value.IsNumeric())
                    {
                        if (typename == typeof(Int16).Name)
                            _Value = (T)(object)Int16.Parse(value.ToString());
                        else if (typename == typeof(Int32).Name)
                            _Value = (T)(object)Int32.Parse(value.ToString());
                        else if (typename == typeof(Int64).Name)
                            _Value = (T)(object)Int64.Parse(value.ToString());
                        else if (typename == typeof(Double).Name)
                            _Value = (T)(object)Double.Parse(value.ToString());
                        else if (typename == typeof(Decimal).Name)
                            _Value = (T)(object)Decimal.Parse(value.ToString());
                        else if (typename == typeof(Single).Name)
                            _Value = (T)(object)Single.Parse(value.ToString());
                    }
                }
            }

            return this;
        }
        
        //[Newtonsoft.Json.JsonConstructor]
        public Field(Object value)
        {
            Set(value);
        }

        public Field(T value)
        {
            _Value = value;
        }

        public IValTest[] Tests { get; set; }

        public Field(IValTest[] tests, object value = null, string name = null)
        {
            FieldName = name ?? "";
            Tests = tests ?? new IValTest[] {};
            init(value);
        }

        private void init(object value = null)
        {
            Errors = new List<string>();
            Set(value); 
        }

        public override string ToString()
        {
            if (Value == null)
                return string.Empty;
            else
                return Value.ToString();
        }


      
    }

    public interface IValTest
    {
        bool Test(object value, List<string> errors);
    }

    public class Required : Attribute, IValTest
    {
        public bool Test(object value, List<String> errors)
        {
            if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
            {
                errors.Add("Required");
                return false;
            }
            
            return true;
        }
    }

    #region Tests

    public class Min : Attribute, IValTest
    {
        private int min;

        public Min(int _min)
        {
            min = _min;
        }

        public bool Test(object v, List<String> errors)
        {
            int value;
            if (v != null && int.TryParse(v.ToString(), out value))
            {
                if (value < min)
                {
                    errors.Add("Too Small");
                    return false;
                }
            }
            return true;
        }
    }

    public class Max : Attribute, IValTest
    {
        private int max;

        public Max(int _max)
        {
            max = _max;
        }

        public bool Test(object v, List<String> errors)
        {
            int? value = (int)v;
            if (!value.HasValue || value > max)
            {
                errors.Add("Too Big");
                return false;
            }
            return true;
        }
    }

    #endregion
}
